package tcp

import (
	"CSCredis/db"
	"CSCredis/global"
	"CSCredis/interface/tcp"
	"CSCredis/logger"
	"CSCredis/server"
	tcpClient "CSCredis/tcp/client"
	"fmt"
	"net"
	"os"
	"os/signal"
	"sync"
	"syscall"
)

/**
 * @Author: hejing@szlabsun.com
 * @Description:
 * @File:  server
 * @Version: 1.0.0
 * @Date: 2021/6/22 15:35
 */
func ListenWithSignal() error {
	closeChan := make(chan int)
	sigCh := make(chan os.Signal)
	signal.Notify(sigCh, syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT)
	go func() {
		// 这个方法即去sigch通道中取数据 没有则阻塞  类似于linux的read()系统调用
		// Block until a signal is received.
		sig := <-sigCh
		switch sig {
		// 如果是如下信号，则向closeChan这个通道写入数据，相应监听函数从通道中读到数据后关闭server的tcp监听
		case syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT:
			closeChan <- 0
		}
	}()
	address := fmt.Sprintf("%s:%d", global.CONFIG.Redis.Ip, global.CONFIG.Redis.Port)

	listen, err := net.Listen("tcp", address)
	if err != nil {
		return err
	}
	logger.Info(fmt.Sprintf("bind: %s, start listening...", address))
	handler := HandlerFactory("tcp")
	_ = doListenWithSignal(handler, listen, closeChan)
	return nil
}
func doListenWithSignal(handler tcp.Handler, listen net.Listener, closeChan <-chan int) error {
	// 起一个协程 当closechan信号获取到数据时  就关闭tcp连接
	go func() {
		<-closeChan
		logger.Info("shutting down...")
		_ = listen.Close()
		_ = handler.Close()
	}()
	defer func() {
		// close during unexpected error
		_ = listen.Close()
		_ = handler.Close()
	}()
	var waitDone sync.WaitGroup // 这是一个阻塞队列计数器 只要里面还有任务没完成 就会阻塞在wait方法
	for {
		conn, err := listen.Accept()
		if err != nil {
			break
		}
		client := tcpClient.NewClient(conn)
		// handle
		logger.Info("accept link")
		waitDone.Add(1)
		go func() {
			defer func() {
				waitDone.Done()
			}()
			handler.Handle(client)
		}()
	}
	waitDone.Wait()
	return nil
}

// 处理器构造工厂 生成对应接口的实现类
func HandlerFactory(handlerType string) tcp.Handler {
	// golang的实现类只要是实现了接口的全部方法就可以是实现类
	if handlerType == "udp" {
		// to udp
	}
	db := db.NewDBImpl()
	return &server.Handler{
		Db: db,
	}
}
